#include <fstream>
#include <thread>
// #include <iostream>

#include "./DbConnPool.h"
#include "./pkg/json.hpp"
#include "./MysqlHandler.h"

namespace DbConnPool
{
    DbConnPool &DbConnPool::getInstance(const std::string &confFilePath)
    {
        static DbConnPool instance(confFilePath); // 静态局部变量，确保全局只有一个实例
        return instance;
    }

    // ConnPool::~ConnPool()
    // {
    //     // 释放队列里管理的MySQL连接资源
    //     while (!this->connQueue_.empty())
    //     {
    //         // MysqlHandler *conn = this->connQueue_.front();
    //         this->connQueue_.pop();
    //         // delete conn;
    //         --this->curSize_;
    //     }
    // }

    // 获取连接
    std::unique_ptr<MysqlHandler, std::function<void(MysqlHandler *)>> DbConnPool::getConn()
    {
        std::unique_lock<std::mutex> lock(this->mtx_);

        if (this->connQueue_.empty())
        {
            while (this->connQueue_.empty()) // 避免可能的虚假唤醒
            {
                // 如果为空，需要阻塞一段时间，等待新的可用连接，produceConn()发现没有可用连接会创建并唤醒
                if (std::cv_status::timeout == this->cond_.wait_for(lock, std::chrono::milliseconds(this->timeout_)))
                {
                    // std::cv_status::timeout 表示超时，队列还为空则继续while()下一次阻塞
                    if (this->connQueue_.empty())
                    {
                        continue;
                    }
                }
            }
        }

        // 有可用的连接
        // 如何还回数据库连接？
        // 使用共享智能指针并规定其删除器->独占智能指针
        // 规定销毁后调用删除器，在互斥的情况下更新空闲时间并加入数据库连接池，再唤醒可能阻塞线程
        // std::shared_ptr<MysqlConn> connptr(connectionQueue_.front(),
        //                                    [this](MysqlConn *conn)
        //                                    {
        //                                        std::lock_guard<std::mutex> locker(mutex_);
        //                                        conn->refreshAliveTime();
        //                                        connectionQueue_.push(conn);
        //                                    });
        // connectionQueue_.pop();
        // cond_.notify_all();
        // return connptr;
        std::unique_ptr<MysqlHandler> conn = std::move(this->connQueue_.front());
        this->connQueue_.pop(); // 尽管是移动，无效指针还需要弹出

        // 定义自定义删除器，删除器需要原始指针
        auto returnConn = [this](MysqlHandler *connPtr)
        {
            std::lock_guard<std::mutex> lock(this->mtx_);

            connPtr->refreshAliveTime();
            this->connQueue_.push(std::unique_ptr<MysqlHandler>(connPtr));

            this->cond_.notify_all(); // 通知有可用连接
        };

        return std::unique_ptr<MysqlHandler, std::function<void(MysqlHandler *)>>(conn.release(), returnConn);
    }

    DbConnPool::DbConnPool(const std::string &confFilePath)
    {
        // assert();
        this->confFilePath_ = confFilePath;
        this->parseJsonConfigFile();

        for (int i = 0; i < this->minSize_; ++i)
        {
            this->addConn();
            // ++this->curSize_;
        }

        // 开启新线程执行任务，一个最大容量内不够就生产，一个超过最大空闲时间回收
        std::thread producer(&DbConnPool::produceConn, this);
        std::thread recycler(&DbConnPool::recycleConn, this);
        // 设置线程分离，不阻塞在此处
        producer.detach();
        recycler.detach();
    }

    // 解析JSON配置文件
    bool DbConnPool::parseJsonConfigFile()
    {
        std::ifstream file(this->confFilePath_);
        // if (!file.is_open())
        // {
        //     std::cerr << "Failed to open file" << std::endl;
        //     return 1;
        // }
        nlohmann::json conf = nlohmann::json::parse(file);

        this->ip_ = conf["ip"];
        this->port_ = conf["port"];
        this->user_ = conf["user"];
        this->password_ = conf["password"];
        this->dbName_ = conf["dbName"];

        this->minSize_ = conf["minSize"];
        this->maxSize_ = conf["maxSize"];
        this->timeout_ = conf["timeout"];
        this->maxIdleTime_ = conf["maxIdleTime"];

        return true;
    }

    void DbConnPool::addConn()
    {
        std::unique_ptr<MysqlHandler> conn = std::make_unique<MysqlHandler>();
        conn->connect(this->ip_, this->port_, this->user_, this->password_, this->dbName_);
        conn->refreshAliveTime(); // 刷新起始的空闲时间点

        this->connQueue_.push(std::move(conn)); // 记录新连接
        ++this->curSize_;
    }

    void DbConnPool::produceConn()
    {
        while (true)
        {
            // RALL手法封装的互斥锁，初始化即加锁，析构即解锁
            std::unique_lock<std::mutex> lock(this->mtx_);

            while (!this->connQueue_.empty()) // 防止可能的虚假唤醒
            {
                // 还有可用连接则不创建，阻塞
                this->cond_.wait(lock);
            }

            // 还没达到连接最大限制，可以创建
            if (this->curSize_ < this->maxSize_)
            {
                this->addConn();
                // ++this->curSize_;

                // 唤醒被阻塞的线程，可以取连接使用
                this->cond_.notify_all();
            }
        }
    }

    // 销毁多余的数据库连接
    void DbConnPool::recycleConn()
    {
        while (true)
        {
            // 周期性的做检测工作，每10000毫秒（10s）执行一次
            std::this_thread::sleep_for(std::chrono::microseconds(10000));

            std::lock_guard<std::mutex> lock(this->mtx_);

            // 连接数位于(minSize, maxSize]时候，可能会有空闲连接等待太久，需要回收
            while (this->connQueue_.size() > this->minSize_)
            {
                std::unique_ptr<MysqlHandler> conn = std::move(this->connQueue_.front());

                if (conn->getAliveTime() >= this->maxIdleTime_)
                {
                    // 存在时间超过设定值则销毁
                    this->connQueue_.pop();
                    // delete conn;
                    --this->curSize_;
                }
                else
                {
                    // 按照先进先出顺序，前面的没有超过后面的肯定也没有，不继续检测而是休眠
                    break;
                }
            }
        }
    }
} // namespace DbConnPool